Traditional multitasking operating systems (e.g., UNIX, Windows) have been implemented in computing environments to provide a way to allocate the resources of the computing environment (e.g., CPU, memory, Input/Output (I/O) devices) among various user programs that may be running simultaneously in the computing environment. The operating system itself comprises a number of functions (executable code) and data structures that may be used to implement the resource allocation services of the operating system.
Operating systems have also been implemented in a so-called “object oriented” manner. That is, when a particular function and/or data structure (defined by a “class” definition) is requested, the operating system creates (“instantiates”) an “object” that uses executable code and/or data structure definitions specified in the class definition. Such objects thus may contain executable code, data structures, or both. Objects that perform actions are typically referred to as “tasks” (also known as “threads”), and a collection of tasks may be referred to as a “process.” Upon loading and execution of the operating system into the computing environment, system tasks and processes will be created in order to support the resource allocation needs of the system. User applications likewise upon execution may cause the creation of tasks (“user tasks”), processes (“user processes”), and other objects in order to perform the actions desired from the application.
In order to protect the operating system and each task running in the computing environment from interference from other tasks also running in the computing environment, typical operating systems apportion the computing environment's execution “space” (e.g., its memory) into a “system” space and a “user” space. The system space generally contains the code and data structures for the operating system tasks, while the user space contains the code and data structures for user tasks. Typically, operating systems are designed so that user tasks cannot directly access the memory apportioned to system tasks. The operating system itself, however, can access all portions of memory.
Conceptually, this “protection model” is illustrated by FIG. 1. In a computer system 1 controlled by an operating system 2, there may be any number of user processes 3 and system tasks 5 executing at one time. User processes 3 each include a number of user tasks 6. Because each user process 3 is only allocated a portion of the system memory, the operating system 2 may restrict access by any user task 6 affiliated with a particular user process 3 to the memory allocated to another process, including the operating system itself. Typically, however, system tasks 5 have unrestricted access to the memory allocated to the operating system 2 and the memory allocated to each user process 3 (indicated by direct connections 7).
Certain operating systems, called “real-time operating systems,” have been developed to provide a more controlled environment for the execution of application programs. Real-time operating systems are designed to be “deterministic” in their behavior—i.e., responses to events can be expected to occur within a known time of the occurrence of the event, without fail. Determinism is particularly necessary in “mission-critical” applications, although it is generally desirable for all operating systems in order to increase reliability. Real-time operating systems are therefore implemented to execute as efficiently as possible with a minimum of execution overhead.
One area where efficiency issues may arise in a real-time multitasking operating system is “context switching.” A context switch causes the CPU to stop executing the currently running task and start executing another task. This context switch may occur for many reasons, for example: the executing task needs to wait for a system resource to become available, or the slice of CPU time allocated to the executing task has been exhausted. When a context switch occurs, information about the state of the currently executing task needs to be saved so that the task may continue executing later without change in the environment. The state information includes control information about the task itself (e.g., register and program counter values), and information related to the state of resources being used by the task (e.g., pointers). In particular, state information for re-entrant functions (i.e., functions that can be used simultaneously by more than one task), needs to be saved during the context switch. The more information that needs to be saved during the context switch, the more time the context switch requires, thus increasing execution overhead.
Task state information is typically maintained in a “task control block” for each task. In order to prevent corruption of task state information by other tasks, the task control block is typically stored in the system space to prevent access by non-system tasks, since most of the information in the task control block is opaque to the task itself (e.g., information needed for context switching purposes only). However, some information stored in the task control block may be needed by tasks during execution (for example, information related to re-entrant functions). In order to access this information, the task must execute a “system call” (e.g., a trap) to invoke a function within the operating systems that will access the desired memory locations within the task control block. Use of the system call thus increases execution overhead (and execution speed). Furthermore, as the number of tasks in the system increases, the number of task control blocks in the operating system increases, thus increasing the overall size of the operating system.